import os
import pyperclip
import pyautogui
import time
import json
import csv
import logging
import win32gui
from verify_directory_comments import verify_directory_comments
import sys
import shutil

# 提示文本常量
COMMENT_PROMPT = "请给这段代码的每一行加中文注释，去掉不必要的空行，只输出完整的代码块给我"

# 获取当前文件的目录
current_directory = os.path.dirname(os.path.abspath(__file__))

# 获取资源文件路径
def get_resource_path(relative_path):
    """获取资源文件的绝对路径，支持开发环境和打包后的环境"""
    try:
        # PyInstaller创建临时文件夹,将路径存储在_MEIPASS
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(os.path.abspath(__file__))
    
    # 确保所有必要的目录都存在
    if any(folder in relative_path for folder in ['log', 'result']):
        # 修改：在打包环境下，将log和result文件夹创建在程序所在目录的上一级
        if hasattr(sys, '_MEIPASS'):
            # 获取程序所在目录的上一级目录
            parent_dir = os.path.dirname(sys.executable)
            resource_dir = os.path.join(parent_dir, os.path.dirname(relative_path))
        else:
            # 开发环境保持不变
            resource_dir = os.path.join(base_path, os.path.dirname(relative_path))
            
        if not os.path.exists(resource_dir):
            os.makedirs(resource_dir, exist_ok=True)
            print(f"创建目录: {resource_dir}")  # 临时使用print替代log_info
        
        # 返回完整路径
        return os.path.join(resource_dir, os.path.basename(relative_path))
    
    # 其他资源文件保持原有逻辑
    full_path = os.path.join(base_path, relative_path)
    if not os.path.exists(full_path) and not any(folder in relative_path for folder in ['log', 'result']):
        print(f"资源文件不存在: {full_path}")  # 临时使用print替代log_error
    return full_path

# 修改图片路径获取方式
recognizeLocation_copy = get_resource_path("recognizeLocation/copy.png")
recognizeLocation_executing = get_resource_path("recognizeLocation/executing.png")
recognizeLocation_unExecute = get_resource_path("recognizeLocation/unExecute.png")
recognizeLocation_reExecute = get_resource_path("recognizeLocation/reExecute.png")
recognizeLocation_tooLong = get_resource_path("recognizeLocation/tooLong.png")

# 设置日志
def setup_logging():
    """设置日志系统，确保日志目录存在"""
    log_dir = get_resource_path("log")  # 使用get_resource_path替代直接路径
    if not os.path.exists(log_dir):
        os.makedirs(log_dir, exist_ok=True)
        print(f"创建日志目录: {log_dir}")  # 临时使用print替代log_info

    # 创建不同级别的日志处理器
    info_handler = logging.FileHandler(os.path.join(log_dir, 'info.log'), encoding='utf-8')
    error_handler = logging.FileHandler(os.path.join(log_dir, 'error.log'), encoding='utf-8')
    debug_handler = logging.FileHandler(os.path.join(log_dir, 'debug.log'), encoding='utf-8')

    # 设置日志格式
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    info_handler.setFormatter(formatter)
    error_handler.setFormatter(formatter)
    debug_handler.setFormatter(formatter)

    # 设置日志级别过滤器
    info_handler.setLevel(logging.INFO)
    error_handler.setLevel(logging.ERROR)
    debug_handler.setLevel(logging.DEBUG)

    # 配置根日志记录器
    root_logger = logging.getLogger()
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(info_handler)
    root_logger.addHandler(error_handler)
    root_logger.addHandler(debug_handler)

# 替换原有的日志配置
# logging.basicConfig(...) 这行删除
setup_logging()

# 全局变量
executed_files = {}
total_files = set()
executed_files_in_run = set()
not_executed_files = set()
result_file_path = get_resource_path("result")
if not os.path.exists(result_file_path):
    os.makedirs(result_file_path, exist_ok=True)
csv_file = os.path.join(result_file_path, "file_execution_status.csv")

# 日志记录
def log_info(message):
    logging.info(message)

def log_error(message):
    logging.error(message)

def collect_files(folder_path, file_extensions):
    matching_files = []  # 用于存储符合条件的文件路径
    # 遍历文件夹并统计文件
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            file_path = os.path.join(root, file)
            # 检查文件后缀是否符合条件
            if any(file_path.endswith(ext) for ext in file_extensions):
                total_files.add(file_path)
                matching_files.append(file_path)

    return matching_files

def save_to_json(file_list, output_json_path):
    if not os.path.exists(output_json_path):
        os.makedirs(os.path.dirname(output_json_path), exist_ok=True)
        with open(output_json_path, 'w', encoding='utf-8') as json_file:
            json.dump(file_list, json_file, ensure_ascii=False, indent=4)

# 更新已执行文件的 JSON，确保即使中断也能保存进度
def update_executed_files_json(folder_path):
    executed_json_name = os.path.join(result_file_path, folder_path.replace("\\", "_").replace(":", "") + "_executed.json")
    with open(executed_json_name, "w", encoding='utf-8') as executed_file:
        json.dump(executed_files, executed_file, ensure_ascii=False, indent=4)

# 写入剪贴板内容到文件
def write_first_clipboard_content_to_file(file_path, folder_path):
    try:
        clipboard_content = pyperclip.paste()  # 获取剪贴板内容
        with open(file_path, 'w', encoding='utf-8') as file:
            # 去掉每行末尾的换行符并写入
            cleaned_content = "".join(line.rstrip() + "\n" for line in clipboard_content.splitlines() if line.strip())
            file.write(cleaned_content)  # 写入文件，避免中文乱码
        # 找到未执行文件
        not_executed_files = total_files - executed_files.keys()
        # 记录已执行文件
        executed_files[file_path] = "Executed"
        update_executed_files_json(folder_path)
        log_info(f"剪贴板内容已写入文件: {file_path}")
         # 更新CSV文件展示执行情况
        update_csv_report(folder_path, len(total_files), len(executed_files), len(not_executed_files), executed_files_in_run, not_executed_files)
        log_info(f"总文件数: {len(total_files)}, 已执行文件数: {len(executed_files_in_run)}, 未执行文件数: {len(not_executed_files)}")
    except Exception as e:
        log_error(f"写入文件时发生错误: {e}")

# 在 if __name__ == "__main__": 之前添加以下辅助函数
def get_screen_position(x_percent, y_percent):
    """
    根据屏幕百分比计算实际坐标
    :param x_percent: 横向百分比 (0-100)
    :param y_percent: 纵向百分比 (0-100)
    :return: (x, y) 坐标元组
    """
    screenWidth, screenHeight = pyautogui.size()
    x = int(screenWidth * (x_percent / 100))
    y = int(screenHeight * (y_percent / 100))
    return x, y

# 静态坐标配置（默认值，基于1920x1080分辨率）
DEFAULT_COORDINATES = {
    'input_box': (977, 982),      # GPT输入框位置
    'regenerate_btn': (1088, 900),  # 重新生成按钮位置
    'close_prompt': (1495, 862),    # 关闭提示按钮位置
    'scroll_area': (1725, 780),     # 滚动区域位置
    'alternate_copy': (1522, 837)   # 备用复制按钮位置
}

def convert_to_dynamic_coordinates(static_coords):
    """
    将静态坐标转换为基于屏幕百分比的动态坐标
    
    Args:
        static_coords (dict): 静态坐标字典
    Returns:
        dict: 转换后的动态坐标字典
    """
    screen_width, screen_height = pyautogui.size()
    
    dynamic_coords = {}
    for key, (x, y) in static_coords.items():
        # 转换为百分比
        x_percent = (x / screen_width) * 100
        y_percent = (y / screen_height) * 100
        dynamic_coords[key] = (x_percent, y_percent)
    
    return dynamic_coords

# 生成动态坐标配置
SCREEN_COORDINATES = convert_to_dynamic_coordinates(DEFAULT_COORDINATES)

# 添加新的检查函数
def default_should_continue():
    """
    默认的继续检查函数，始终返回 True
    """
    return True

def check_should_continue(should_continue_fn):
    """
    检查是否应该继续执行
    
    Args:
        should_continue_fn (function): 检查函数，返回 True/False
    Returns:
        bool: 是否继续执行
    """
    try:
        return should_continue_fn()
    except Exception as e:
        log_error(f"检查是否继续执行时发生错误: {e}")
        return True  # 发生错误时默认继续执行

# 处理单个文件
def process_file(file_path, folder_path, should_continue_fn=default_should_continue):
    try:
        # 检查是否应该继续执行
        if not check_should_continue(should_continue_fn):
            log_info("收到停止信号，终止处理")
            return False

        # 打开文件并读取内容
        with open(file_path, 'r', encoding='utf-8') as file:
            file_content = file.read()

        # 将内容复制到剪贴板并请求加注释
        send_content = COMMENT_PROMPT + file_content
        pyperclip.copy(send_content)
        log_info(f"文件内容已复制到剪贴板: {file_path}")
        
        # 模拟操作
        time.sleep(0.5)
        input_pos = get_screen_position(*SCREEN_COORDINATES['input_box'])
        pyautogui.click(input_pos[0], input_pos[1])  # ���击gpt输入框
        time.sleep(0.5)
        pyautogui.hotkey('ctrl', 'v')  # 粘贴内容
        time.sleep(0.5)
        pyautogui.press('enter')  # 确认操作
        # 循环监听
        loop_start_time = time.time()
        # 监听图标消失再执行这后面的程序
        while True:
            try:
                # 监听未执行图标消失再执行这后面的程序
                executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_unExecute, confidence=0.9))
                if time.time() - loop_start_time > 60:
                    pyautogui.press('enter')  # 确认操作
                    result = 1/0
                    log_info(f"用异常结束未发送图标的监听循环")  
            except Exception as e:
                log_info(f"步骤1：结束未发送图标的监听: {e}")
                break
        while True:
            try:
                # 监听执行图标出现再执行这后面的程序
                executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_executing, confidence=0.95))
                if time.time() - loop_start_time > 180:
                    result = 1/0
                    log_info(f"用异常退出循环1")  
            except Exception as e:
                log_info(f"步骤2：结束执行图标的监听1: {e}")
                time.sleep(5)
                # 判断是否有重新生成的图标
                while True:
                    try:
                        # 监听重新生成标出现再执行这后面的程序
                        executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_reExecute, confidence=0.9))
                        log_info(f"特殊情况1：监听到有重新生成的图标")
                         # 监听是否有消息过长的提示
                        while True:
                            try:
                                #如果监听到消息过长的提示就结束程序
                                executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_tooLong, confidence=0.1))
                                log_info(f"特殊情况2：监听到消息过长结束对该文件的注释")
                                # 找到未执行文件
                                not_executed_files = total_files - executed_files.keys()
                                # 记录已执行文件
                                executed_files[file_path] = "Executed"
                                update_executed_files_json(folder_path)
                                log_info(f"剪贴板内容已写入文件: {file_path}")
                                # 更新CSV文件展示执行情况
                                update_csv_report(folder_path, len(total_files), len(executed_files), len(not_executed_files), executed_files_in_run, not_executed_files)
                                log_info(f"总文件数: {len(total_files)}, 已执行文件数: {len(executed_files_in_run)}, 未执行文件数: {len(not_executed_files)}")
                                pyautogui.press('f5')
                                time.sleep(10)
                                return  True
                            except Exception as e:
                                log_info(f"未捕获到消息过长的问题，继续执行: {e}")
                                break           # 点击F5刷新页面
                        log_info("消息过长之后，开始刷新页面...")
                        time.sleep(600)
                        pyautogui.press('f5')
                        time.sleep(10)
                        # 刷新页面重新输入内容生成回复
                        log_info("消息过长之后，重新输入内容...")
                        pyautogui.click(input_pos[0], input_pos[1])  # 点击gpt输入框
                        time.sleep(0.5)
                        pyautogui.hotkey('ctrl', 'v')  # 粘贴内容
                        time.sleep(0.5)
                        pyautogui.press('enter')  # 确认操作
                        log_info("消息过长之后，内容已重新发送")
                        if time.time() - loop_start_time > 650:
                            result = 1/0
                            log_info(f"用异常退出循环1")  
                    except Exception as e:
                        log_info(f"步骤3：结束监听重新生成图标的监听，防止bug: {e}")
                        # 点击重新生成
                        regenerate_pos = get_screen_position(*SCREEN_COORDINATES['regenerate_btn'])
                        pyautogui.click(regenerate_pos[0], regenerate_pos[1])
                        time.sleep(2)
                        pyautogui.click(regenerate_pos[0], regenerate_pos[1])
                        # 点击关闭提示或者继续生成
                        close_prompt_pos = get_screen_position(*SCREEN_COORDINATES['close_prompt'])
                        pyautogui.click(close_prompt_pos[0], close_prompt_pos[1])
                        # 监听执行图标出现再执行这后面程序
                        time.sleep(20)
                        while True:
                            try:
                                executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_executing, confidence=0.95))
                                 # 监听是否有消息过长的提示
                                while True:
                                    try:
                                        #如果监听到消息过长的提示就结束程序
                                        executing_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_tooLong, confidence=0.5))
                                        log_info(f"特殊情况3：监听到消息过长结束对该文件的注释")
                                        pyautogui.press('f5')
                                        time.sleep(10)
                                        return True
                                    except Exception as e:
                                        log_info(f"特殊情况4：未捕获到消息过长的问题，继续执行: {e}")
                                        break   
                                if time.time() - loop_start_time > 180:
                                    result = 1/0
                                    log_info(f"特殊情况5：用异常退出循环2")  
                            except Exception as e:
                                log_info(f"步骤4：结束执行图标的监听2: {e}")
                                break
                        scroll_pos = get_screen_position(*SCREEN_COORDINATES['scroll_area'])
                        pyautogui.click(scroll_pos[0], scroll_pos[1])
                        time.sleep(2)
                        pyautogui.scroll(-20000)  # 向滚动
                        time.sleep(2)
                        try:
                            # 记录复制代码的位置
                            copy_locations = list(pyautogui.locateAllOnScreen(recognizeLocation_copy, confidence=0.5))
                            log_info(f"步骤5：记录复制代码的位置: {copy_locations}.")
                            if copy_locations:
                                if len(copy_locations) == 1:
                                    # 只有一个匹配项，直接使用它
                                    target_location = copy_locations[0]
                                else:
                                    # 找到y坐标最大的图像
                                    target_location = max(copy_locations, key=lambda loc: loc.top)
                                # 获取中心点坐标
                                center = pyautogui.center(target_location)
                                log_info(f"步骤6：找到了复制代码的位置: {center}.")
                                # 单击该位置
                                pyautogui.click(center)
                                # 检测剪切板内容是否有变化
                                new_clipboard_content = pyperclip.paste()
                                if new_clipboard_content != send_content:
                                    log_info("步骤7：剪切板内容已更新")
                                    write_first_clipboard_content_to_file(file_path, folder_path)  # 写入剪贴板内容
                                else:
                                    log_info("剪切板内容未复制成功结束程序")
                                    log_info(f"--------结束执行--------")
                                    return False
                        except Exception as e:
                            log_error(f"在记录复制代码的位置时发生错误: {e}")
                            time.sleep(2)
                            alternate_pos = get_screen_position(*SCREEN_COORDINATES['alternate_copy'])
                            pyautogui.click(alternate_pos[0], alternate_pos[1])
                            start_time = time.time()
                            reNewCount = 0
                            while True:
                                if time.time() - start_time > 300:
                                    log_info("超过五分钟，重新启动其他操作")
                                    pyautogui.hotkey('ctrl', 'shift', 'k')  # 假设某个重启键
                                    reNewCount += 3
                                    time.sleep(30)
                                    start_time = time.time()
                                    pyautogui.click(alternate_pos[0], alternate_pos[1])
                                    time.sleep(2)
                                    pyautogui.scroll(10 + reNewCount * 5)
                                pyautogui.scroll(10)  # 模拟滚动操作
                                pyautogui.click()
                                time.sleep(2)
                                # 检测剪切板内容是否有变化
                                new_clipboard_content = pyperclip.paste()
                                if new_clipboard_content != send_content:
                                    log_info("剪切板内容已更新")
                                    write_first_clipboard_content_to_file(file_path, folder_path)  # 写入剪贴板内容
                                else:
                                    log_info("剪切板内容未复制成功，结束程序")
                                    log_info(f"--------结束执行--------")
                                    return False
                                # 检测鼠标指针状态，判断是否需要写入文件
                                if win32gui.GetCursorInfo()[1] == 65567:  # 假设65567是点击超链接状态
                                    log_info("鼠标指针处于点击超链接状态")
                                    time.sleep(2)
                                    pyautogui.click()
                                    write_first_clipboard_content_to_file(file_path, folder_path)  # 写入剪贴板内容
                                    return False
                        break     
                break  # 捕获错误并退出循环
    except Exception as e:
        log_error(f"处理文件时发生错误: {e}")
    return True

# 处理文件夹并生成CSV报告
def process_folder(folder_path, file_extensions, should_continue_fn=default_should_continue):
    try:
        # 检查是否应该继续执行
        if not check_should_continue(should_continue_fn):
            log_info("收到停止信号，终止处理")
            return False

        # 创建备份文件夹
        original_folder = folder_path + "_Original"
        if not os.path.exists(original_folder):
            shutil.copytree(folder_path, original_folder)
            log_info(f"已创建原始文件备份: {original_folder}")
            
        # 遍历文件夹并统计文件
        for root, dirs, files in os.walk(folder_path):
            for file in files:
                file_path = os.path.join(root, file)
                if any(file_path.endswith(ext) for ext in file_extensions):  # 只处理指定类型的文件
                    if file_path not in executed_files:  # 跳过已执行文件
                        log_info(f"开始处理文件: {file_path}")
                        if not process_file(file_path, folder_path, should_continue_fn):
                            return False
                        executed_files_in_run.add(file_path)
                    else:
                        # log_info(f"文件已执行: {file_path}")  # 跳过已执行文件
                        pass
        
        # 更新CSV文件展示执行情况
        update_csv_report(folder_path,len(total_files), len(executed_files), len(not_executed_files), executed_files_in_run, not_executed_files)
        log_info(f"处理文件夹完成: {folder_path}, 总文件数: {len(total_files)}, 已执行: {len(executed_files)}, 未执行: {len(not_executed_files)}")
        log_info(f"--------结束执行--------")
    except Exception as e:
        log_error(f"处理文件夹时发生错误: {e}")
        log_info(f"--------结束执行--------")
    return True

# 更新CSV报告
def update_csv_report(folder_path, total_count, executed_count, not_executed_count, executed_files, not_executed_files):
    try:
        with open(csv_file, mode="w", newline='', encoding='utf-8') as file:
            writer = csv.writer(file)
            writer.writerow(["文件夹路径", "总文件数","已执行文件数", "未执行文件数"])
            writer.writerow([folder_path, total_count, executed_count, not_executed_count])
            writer.writerow(["已执行文件列表"])
            writer.writerows([[f] for f in executed_files])
            writer.writerow(["未执行文件列表"])
            writer.writerows([[f] for f in not_executed_files])
            writer.writerow([])  # 添加空行
        log_info(f"已更新CSV文件: {csv_file}")
    except Exception as e:
        log_error(f"更新CSV文件时发生错误: {e}")

# 默认提示文本
DEFAULT_PROMPT = "请给这段代码的每一行加中文注释，去掉不必要的空行，只输出完整的代码块给我"

def merge_coordinates(custom_coords):
    """
    合并默认坐标和自定义坐标
    
    Args:
        custom_coords (dict): 自定义坐标字典，可以只包含需要覆盖的坐标
    Returns:
        dict: 合并后的坐标字典
    """
    merged = DEFAULT_COORDINATES.copy()
    if custom_coords:
        merged.update(custom_coords)
    return merged

# 新增主函数用于外部调用
def generate_comments(folder_paths, file_types, coordinates=None, prompt=None, should_continue=default_should_continue):
    """
    为指定文件夹中的代码文件生成注释
    
    Args:
        folder_paths (list): 需要处理的文件夹路径列表
        file_types (list): 需要处理的文件类型列表 (如 ['.java', '.ts', '.vue'])
        coordinates (dict, optional): 自定义坐标配置
        prompt (str, optional): 自定义提示文本
        should_continue (function): 检查函数，返回 True/False 表示是否继续运行
    Returns:
        bool: 执行是否成功完成
    """
    try:
        log_info(f"--------开始执行--------")
        
        # 设置提示文本
        global COMMENT_PROMPT
        COMMENT_PROMPT = prompt if prompt is not None else DEFAULT_PROMPT
        
        # 合并并设置坐标配置
        static_coords = merge_coordinates(coordinates)
        global SCREEN_COORDINATES
        SCREEN_COORDINATES = convert_to_dynamic_coordinates(static_coords)
        
        # 记录使用的配置
        log_info(f"使用的提示文本: {COMMENT_PROMPT}")
        log_info(f"使用的坐标配置: {static_coords}")
        
        # 获取屏幕的宽度和高度
        screenWidth, screenHeight = pyautogui.size()
        # 切屏
        # pyautogui.hotkey('alt', 'tab')
        
        # 处理每个文件夹
        for folder_path in folder_paths:
            # 检查是否应该继续执行
            if not check_should_continue(should_continue):
                log_info("收到停止信号，终止处理")
                return False
                
            # 清空集合
            total_files.clear()
            executed_files_in_run.clear()
            not_executed_files.clear()
            
            global csv_file
            csv_file = os.path.join(result_file_path, folder_path.replace("\\", "_").replace(":", "") + "_execution_status.csv")
            
            # 记录文件夹下符合条件的所有文件
            matching_files = collect_files(folder_path, file_types)
            
            # 将文件列表写入 JSON 文件
            total_json_name = os.path.join(result_file_path, folder_path.replace("\\", "_").replace(":", "") + "_total.json")
            save_to_json(matching_files, total_json_name)
            
            # 加载已执行文件录
            executed_json_name = os.path.join(result_file_path, folder_path.replace("\\", "_").replace(":", "") + "_executed.json")
            if os.path.exists(executed_json_name):
                with open(executed_json_name, "r", encoding='utf-8') as file:
                    global executed_files
                    executed_files = json.load(file)
                    
            if not process_folder(folder_path, file_types, should_continue):
                return False
                
            # 验证步骤
            if check_should_continue(should_continue):
                log_info(f"开始验证文件注释")
                verify_directory_comments(folder_path, file_types)
                log_info(f"完成文件注释验证")
        
        log_info("所有任务执行完成")
        return True
        
    except Exception as e:
        log_error(f"执行过程中发生错误: {str(e)}")
        return False

if __name__ == "__main__":
    # 原有的直接执行代码
    folder_paths = ["D:\\A-java语言\\SpringBoot\\sourcelearn\\spring-boot-3.3.x"]
    file_types = ['.java', '.ts', '.vue']
    
    # 示例1：使用默认配置
    generate_comments(folder_paths, file_types)
    
    # 示例2：只修改部分坐标和提示文本
    custom_coordinates = {
        'input_box': (900, 900),
        'scroll_area': (1700, 700)
    }
    custom_prompt = "请为代码添加详细的中文注释，保持代码格式不变"
    generate_comments(folder_paths, file_types, custom_coordinates, custom_prompt)


